/*
 * Created on 2005-6-6
 * base on ust calculation.pas
 */
package com.hd123.rumba.util;

import java.math.BigDecimal;
import java.math.RoundingMode;

import com.hd123.rumba.exception.ParameterException;

/**
 * @author caili
 */
public class Calculation {
  public static final BigDecimal ZeroTolerance = new BigDecimal(0.0001);
  public static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;
  public static final int SCALE_MONEY = 2;
  public static final int SCALE_PRICE = 4;
  public static final int SCALE_QTY = 3;

  public static boolean isZero(BigDecimal a) {
    return a.abs().subtract(BigDecimal.ZERO).compareTo(ZeroTolerance) <= 0;
  }

  public static TRQuantityPriceTotal calculateQuantityPriceTotal(TRQuantityPriceTotal qpt)
      throws ParameterException {
    TRQuantityPriceTotal r = new TRQuantityPriceTotal(qpt);
    int fixedCount;
    // BigDecimal f = BigDecimal.ZERO;
    if (r.getTaxRate() == null)
      throw new ParameterException("税率没有提供, 不能计算");

    // f = r.getTaxRate();
    if ((r.getTaxRate().doubleValue() < 0) || (r.getTaxRate().doubleValue() >= 1))
      throw new ParameterException("税率(" + r.getTaxRate() + ")错误, 不能计算.\n税率应大于等于0小于1");
    fixedCount = 0;
    if (r.isQuantityFixed())
      ++fixedCount;
    if (r.isUnitPriceFixed())
      ++fixedCount;
    if (r.isGrossUnitPriceFixed())
      ++fixedCount;
    if (r.isAmountFixed())
      ++fixedCount;
    if (r.isTotalFixed())
      ++fixedCount;
    if (r.isTaxFixed())
      ++fixedCount;
    if (fixedCount != 2)
      throw new ParameterException("CalculateQuantityPricetotal: 参数中固定值必须是两个(输入参数中有" + fixedCount
          + "个)");

    if (r.isQuantityFixed() && r.isUnitPriceFixed()) {
      // 数量, 去税单价 -> 含税单价, 含税金额, 去税金额, 税额
      if (r.getQuantity() == null || r.getUnitPrice() == null)
        return r;
      r.setAmount(r.getQuantity().multiply(r.getUnitPrice()).setScale(SCALE_MONEY, ROUNDING_MODE));
      r.setTax(r.getAmount().multiply(r.getTaxRate()).setScale(SCALE_MONEY, ROUNDING_MODE));
      r.setTotal(r.getAmount().add(r.getTax()));
      if (isZero(r.getQuantity()))
        r.setGrossUnitPrice(r.getUnitPrice().multiply(BigDecimal.ONE.add(r.getTaxRate())).setScale(
            SCALE_PRICE, ROUNDING_MODE));
      else
        r.setGrossUnitPrice(r.getTotal().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
    } else if (r.isQuantityFixed() && r.isGrossUnitPriceFixed()) {
      // 数量, 含税单价 -> 去税单价, 含税金额, 去税金额, 税额
      if (r.getQuantity() == null || r.getGrossUnitPrice() == null)
        return r;
      r.setTotal(r.getQuantity().multiply(r.getGrossUnitPrice()).setScale(SCALE_MONEY,
          ROUNDING_MODE));
      r.setAmount(r.getTotal().divide(BigDecimal.ONE.add(r.getTaxRate()), SCALE_MONEY,
          ROUNDING_MODE));
      r.setTax(r.getTotal().subtract(r.getAmount()));
      if (isZero(r.getQuantity()))
        r.setUnitPrice(r.getGrossUnitPrice().divide(BigDecimal.ONE.add(r.getTaxRate()),
            SCALE_PRICE, ROUNDING_MODE));
      else
        r.setUnitPrice(r.getAmount().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
    } else if (r.isQuantityFixed && r.isAmountFixed) {
      // 数量, 去税金额 -> 含税单价, 去税单价, 含税金额, 税额
      if (r.getQuantity() == null || r.getAmount() == null)
        return r;
      if (isZero(r.getQuantity()))
        return r;
      r.setTax(r.getAmount().multiply(r.getTaxRate()).setScale(SCALE_MONEY, ROUNDING_MODE));
      r.setTotal(r.getAmount().add(r.getTax()));
      r.setUnitPrice(r.getAmount().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
      r.setGrossUnitPrice(r.getTotal().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
    } else if (r.isQuantityFixed() && r.isTotalFixed()) {
      // 数量, 含税金额 -> 含税单价, 去税单价, 去税金额, 税额
      if (r.getQuantity() == null || r.getTotal() == null)
        return r;
      if (isZero(r.getQuantity()))
        return r;
      r.setAmount(r.getTotal().divide(BigDecimal.ONE.add(r.getTaxRate()), SCALE_MONEY,
          ROUNDING_MODE));
      r.setTax(r.getTotal().subtract(r.getAmount()));
      r.setUnitPrice(r.getAmount().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
      r.setGrossUnitPrice(r.getTotal().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
    } else if (r.isUnitPriceFixed() && r.isAmountFixed()) {
      // 去税单价, 去税金额 -> 数量, 含税单价, 含税金额, 税额
      if (null == r.getUnitPrice() || null == r.getAmount())
        return r;
      if (isZero(r.getUnitPrice()))
        return r;
      r.setTax(r.getAmount().multiply(r.getTaxRate()).setScale(SCALE_MONEY, ROUNDING_MODE));
      r.setTotal(r.getAmount().add(r.getTax()));
      r.setQuantity(r.getAmount().divide(r.getUnitPrice(), SCALE_QTY, ROUNDING_MODE));
      r.setGrossUnitPrice(r.getTotal().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
    } else if (r.isUnitPriceFixed && r.isTotalFixed) {
      // 去税单价, 含税金额 -> 数量, 含税单价, 去税金额, 税额
      if (null == r.getUnitPrice() || null == r.getTotal())
        return r;
      if (isZero(r.getUnitPrice()))
        return r;
      r.setAmount(r.getTotal().divide(BigDecimal.ONE.add(r.getTaxRate()), SCALE_MONEY,
          ROUNDING_MODE));
      r.setTax(r.getTotal().subtract(r.getAmount()));
      r.setQuantity(r.getAmount().divide(r.getUnitPrice(), SCALE_QTY, ROUNDING_MODE));
      r.setGrossUnitPrice(r.getTotal().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
    } else if (r.isGrossUnitPriceFixed && r.isAmountFixed) {
      // 含税单价, 去税金额 -> 数量, 去税单价, 含税金额, 税额
      if (null == r.getGrossUnitPrice() || null == r.getAmount())
        return r;
      if (isZero(r.getGrossUnitPrice()))
        return r;
      r.setTax(r.getAmount().multiply(r.getTaxRate()).setScale(SCALE_MONEY, ROUNDING_MODE));
      r.setTotal(r.getAmount().add(r.getTax()));
      r.setQuantity(r.getTotal().divide(r.getGrossUnitPrice(), SCALE_QTY, ROUNDING_MODE));
      r.setGrossUnitPrice(r.getTotal().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
    } else if (r.isGrossUnitPriceFixed && r.isTotalFixed) {
      // 含税单价, 含税金额 -> 数量, 去税单价, 去税金额, 税额
      if (null == r.getGrossUnitPrice() || null == r.getTotal())
        return r;
      if (isZero(r.getGrossUnitPrice()))
        return r;
      r.setAmount(r.getTotal().divide(BigDecimal.ONE.add(r.getTaxRate()), SCALE_MONEY,
          ROUNDING_MODE));
      r.setTax(r.getTotal().subtract(r.getAmount()));
      r.setQuantity(r.getTotal().divide(r.getGrossUnitPrice(), SCALE_QTY, ROUNDING_MODE));
      if (isZero(r.getQuantity())) {
        r.setUnitPrice(r.getGrossUnitPrice().divide(BigDecimal.ONE.add(r.getTaxRate()),
            SCALE_MONEY, ROUNDING_MODE));
      } else {
        r.setUnitPrice(r.getAmount().divide(r.getQuantity(), SCALE_PRICE, ROUNDING_MODE));
      }
    } else if (r.isAmountFixed && r.isTotalFixed) {
      // 去税税额, 含税金额 -> 税额
      r.setTax(r.getTotal().subtract(r.getAmount()));
    } else if (r.isTaxFixed && r.isTotalFixed) {
      // 税额, 含税金额 -> 去税金额
      r.setAmount(r.getTotal().subtract(r.getTax()));
    } else
      throw new ParameterException("CalculateQuantityPriceTotal: 输入参数不合法.");
    return r;
  }

  public static TRQuantity qtyToStr(BigDecimal qty, BigDecimal qpc) {
    TRQuantity result = new TRQuantity();
    // qty = qty.setScale(SCALE_QTY,
    // ROUNDING_MODE);
    result.setQpc(qpc);
    result.setQty(qty);
    if (isZero(qpc))
      result.setQtyStr(qty.toString());
    else {
      BigDecimal dr[] = qty.abs().divideAndRemainder(qpc);
      BigDecimal i = dr[0].setScale(0);
      BigDecimal j = dr[1];
      // if (j.precision() > 3)
      // j.setScale(SCALE_QTY, ROUNDING_MODE);
      if (qty.compareTo(BigDecimal.ZERO) > 0) {
        if (isZero(j))
          result.setQtyStr(i.toString());
        else
          result.setQtyStr(i.toString() + "+" + j.toString());
      } else if (qty.compareTo(BigDecimal.ZERO) == 0) {
        result.setQtyStr("0");
      } else {
        if (isZero(j))
          result.setQtyStr("-" + i.toString());
        else
          result.setQtyStr("-(" + i.toString() + "+" + j.toString() + ")");
      }
    }
    return result;
  }

  /**
   * 允许的格式有2, -2, 2+2, -(2+2)
   * 
   * @param qtyStr
   * @param aqpc
   * @return TRQuantity
   */
  public static TRQuantity strToQty(String qtyStr, BigDecimal aqpc) {
    TRQuantity result = new TRQuantity();

    String s1, s2;
    int i, j, k, iFlag = 1;

    BigDecimal qpc = isZero(aqpc) ? BigDecimal.ONE : aqpc;
    result.setQpc(qpc);
    qtyStr = qtyStr.replaceAll(" ", "").trim();
    j = qtyStr.indexOf('(');
    k = qtyStr.indexOf(')');
    try {
      if (j >= 0 && k >= 0) {
        String s0 = qtyStr.substring(0, j).trim();
        if (s0.length() > 1
            || (s0.length() == 1 && s0.getBytes()[0] != '+' && s0.getBytes()[0] != '-'))
          throw new Exception("error");
        if (k != qtyStr.length() - 1)
          throw new Exception("Error");
        iFlag = 1;
        if (s0.equals("-"))
          iFlag = -1;
        i = qtyStr.indexOf("+");
        if (i <= 0) {
          s1 = qtyStr.substring(j + 1, k);
          s2 = "0";
        } else {
          s1 = qtyStr.substring(j + 1, i).trim();
          s2 = qtyStr.substring(i + 1, k).trim();
        }
      } else if (j == -1 && k == -1) {
        i = qtyStr.indexOf("-");
        if (i != -1) {
          if (i == 0) {
            qtyStr = "-(" + qtyStr.substring(1) + ")";
            return strToQty(qtyStr, qpc);
          } else
            throw new Exception("Error");
        }

        i = qtyStr.indexOf("+");
        if (i == -1) {
          s1 = qtyStr;
          s2 = "0";
        } else if (i == 0) {
          s1 = "0";
          s2 = qtyStr.substring(1);
        } else {
          s1 = qtyStr.substring(0, i).trim();
          s2 = qtyStr.substring(i + 1).trim();
        }
      } else
        throw new Exception("Error");

      BigDecimal flag = new BigDecimal(iFlag);
      result.setQty(flag.multiply(new BigDecimal(s1)).multiply(qpc).add(
          flag.multiply(new BigDecimal(s2))));
      result.setQtyStr(qtyToStr(result.getQty(), qpc).getQtyStr());
    } catch (Exception e) {
      result.setQty(BigDecimal.ZERO);
      result.setQtyStr("0");
    }

    return result;
  }

  /**
   * 分摊算法. 注意: totals会被舍入到scale指定的小数点位数.<br>
   * 如果ratios之和等于0, 则平摊. 没有考虑ratios元素有正有负的情况, 假设它们都是正的或零.
   * 
   * @param totals
   *            N个待分摊的数值
   * @param ratios
   *            K个成分的参考数值(>=0), 按此参考数值的比例分摊.
   * @param scale
   *            分摊结果的小数点位数
   * @return result[i][j]是第N个待分摊的数值分摊到第j个成分上的数值
   */
  public static BigDecimal[][] apportion(BigDecimal[] totals, BigDecimal[] ratios, int scale) {
    BigDecimal results[][] = new BigDecimal[totals.length][ratios.length];
    for (int i = 0; i < totals.length; ++i) {
      totals[i] = totals[i].setScale(scale, ROUNDING_MODE);
    }
    BigDecimal ratioSum = BigDecimal.ZERO;
    int maxRatioIndex = 0;
    for (int i = 0; i < ratios.length; ++i) {
      ratioSum = ratioSum.add(ratios[i].setScale(scale, ROUNDING_MODE));
      if (ratios[maxRatioIndex].compareTo(ratios[i]) < 0)
        maxRatioIndex = i;
    }

    if (isZero(ratioSum)) {
      ratioSum = new BigDecimal(ratios.length);
      for (int i = 0; i < ratios.length; ++i)
        ratios[i] = BigDecimal.ONE;
    }

    BigDecimal portionSum[] = new BigDecimal[totals.length];
    for (int i = 0; i < totals.length; ++i) {
      portionSum[i] = BigDecimal.ZERO;
      for (int j = 0; j < ratios.length; ++j) {
        results[i][j] = totals[i].multiply(ratios[j]).divide(ratioSum, scale, ROUNDING_MODE);
        portionSum[i] = portionSum[i].add(results[i][j]);
      }
      results[i][maxRatioIndex] = results[i][maxRatioIndex].add(totals[i].subtract(portionSum[i]))
          .setScale(scale, ROUNDING_MODE);
    }
    return results;
  }

  /**
   * 分摊算法. BigDecimal[][] BigDecimal[][] apportion(BigDecimal[] totals,
   * BigDecimal[] factors, int scale)的简化版. 其中scale=2.
   * 
   * @param total
   * @param ratios
   * @return 分摊后的数组
   */
  public static BigDecimal[] apportion(BigDecimal total, BigDecimal[] ratios) {
    BigDecimal[] totals = {
      total };
    int scale = 2;
    BigDecimal[][] results = apportion(totals, ratios, scale);
    return results[0];
  }
}
